#include "common.h"
#include "errno.h"
#include "termios.h"

#include "linux/sched.h"
#include "linux/kernel.h"
#include "linux/tty.h"
#include "asm/segment.h"

static void change_speed(struct tty_struct *tty)
{
    
}

static void flush(struct tty_queue *queue)
{
    sysctl_disable_irq();
    queue -> head = queue -> tail;
    sysctl_enable_irq();
}

static void wait_until_sent(struct tty_struct *tty)
{
    //do nothing - not implemented
}

static void send_break(struct tty_struct *tty)
{
    //do nothing - not implemented
}

static int get_termios(struct tty_struct *tty,struct termios *termios)
{
    char *from = (char *)&(tty -> termios);
    char *to = termios;

    verify_area(termios,sizeof(*termios));
    mem_copy_from_kernel((ulong)&(tty -> termios),(ulong)termios,sizeof(*termios));
    return 0;
}

static int set_termios(struct tty_struct *tty,struct termios *termios)
{
    mem_copy_to_kernel((ulong)termios,(ulong)&(tty -> termios),sizeof(*termios));
    change_speed(tty);
    return 0;
}

static int get_termio(struct tty_struct *tty,struct termio *termio)
{
    int i;
    struct termio tmp_termio;

    verify_area(termio,sizeof(*termio));
    tmp_termio.c_iflag = tty -> termios.c_iflag;
    tmp_termio.c_oflag = tty -> termios.c_oflag;
    tmp_termio.c_cflag = tty -> termios.c_cflag;
    tmp_termio.c_lflag = tty -> termios.c_lflag;
    tmp_termio.c_line = tty -> termios.c_line;

    for(i = 0;i < NCC;i++)
    {
        tmp_termio.c_cc[i] = tty -> termios.c_cc[i];
    }

    mem_copy_from_kernel((ulong)&tmp_termio,(ulong)termio,sizeof(*termio));
    return 0;
}

static int set_termio(struct tty_struct *tty,struct termio *termio)
{
    int i;
    struct termio tmp_termio;

    mem_copy_to_kernel((ulong)termio,(ulong)&tmp_termio,sizeof(*termio));
    *(uint16_t *)&tty -> termios.c_iflag = tmp_termio.c_iflag;
    *(uint16_t *)&tty -> termios.c_oflag = tmp_termio.c_oflag;
    *(uint16_t *)&tty -> termios.c_cflag = tmp_termio.c_cflag;
    *(uint16_t *)&tty -> termios.c_lflag = tmp_termio.c_lflag;
    tty -> termios.c_line = tmp_termio.c_line;

    for(i = 0;i < NCC;i++)
    {
        tty -> termios.c_cc[i] = tmp_termio.c_cc[i];
    }

    change_speed(tty);
    return 0;
}

int tty_ioctl(int dev,int cmd,int64_t arg)
{
    struct tty_struct *tty;
    ulong v;

    if(MAJOR(dev) == 5)
    {
        dev = current -> tty;

        if(dev < 0)
        {
            panic("tty_ioctl: dev<0");
        }
    }
    else
    {
        dev = MINOR(dev);
    }

    tty = dev + tty_table;

    switch(cmd)
    {
        case TCGETS:
            return get_termios(tty,(struct termios *)arg);

        case TCSETSF:
            flush(&tty -> read_q);//fallthrough
            flush(&tty -> secondary);

        case TCSETSW:
            wait_until_sent(tty);//fallthrough

        case TCSETS:
            return set_termios(tty,(struct termios *)arg);

        case TCGETA:
            return get_termio(tty,(struct termio *)arg);
        
        case TCSETAF:
            flush(&tty -> read_q);//fallthrough
            flush(&tty -> secondary);

        case TCSETAW:
            wait_until_sent(tty);//fallthrough;

        case TCSETA:
            return set_termio(tty,(struct termio *)arg);

        case TCSBRK:
            if(!arg)
            {
                wait_until_sent(tty);
                send_break(tty);
            }

            return 0;

        case TCXONC:
            return -EINVAL;//no implemented

        case TCFLSH:
            if(arg == 0)
            {
                flush(&tty -> read_q);
                flush(&tty -> secondary);
            }
            else if(arg == 1)
            {
                flush(&tty -> write_q);
            }
            else if(arg == 2)
            {
                flush(&tty -> read_q);
                flush(&tty -> secondary);
                flush(&tty -> write_q);
            }
            else
            {
                return -EINVAL;
            }

            return 0;

        case TIOCEXCL:
            return -EINVAL;//not implemented

        case TIOCNXCL:
            return -EINVAL;//not implemented

        case TIOCSCTTY:
            return -EINVAL;//set controlling term NI

        case TIOCGPGRP:
            verify_area((void *)arg,sizeof(tty -> pgrp));
            mem_copy_from_kernel((ulong)&tty -> pgrp,(ulong *)arg,sizeof(tty -> pgrp));
            return 0;

        case TIOCSPGRP:
            mem_copy_to_kernel((ulong *)arg,(ulong)&tty -> pgrp,sizeof(tty -> pgrp));
            return 0;

        case TIOCOUTQ:
            verify_area((void *)arg,sizeof(v));
            v = CHARS(tty -> write_q);
            mem_copy_from_kernel((ulong)&v,(ulong *)arg,sizeof(v));
            return 0;

        case TIOCINQ:
            verify_area((void *)arg,sizeof(v));
            v = CHARS(tty -> secondary);
            mem_copy_from_kernel((ulong)&v,(ulong *)arg,sizeof(v));
            return 0;

        case TIOCSTI:
            return -EINVAL;//not implemented

        case TIOCGWINSZ:
            return -EINVAL;//not implemented

        case TIOCSWINSZ:
            return -EINVAL;//not implemented

        case TIOCMGET:
            return -EINVAL;//not implemented

        case TIOCMBIS:
            return -EINVAL;//not implemented

        case TIOCMBIC:
            return -EINVAL;//not implemented

        case TIOCMSET:
            return -EINVAL;//not implemented

        case TIOCGSOFTCAR:
            return -EINVAL;//not implemented

        case TIOCSSOFTCAR:
            return -EINVAL;//not implemented

        default:
            return -EINVAL;
    }
}